defmodule DeeprHelpLib do
	@moduledoc false

	def match(_caller_mod, _user, rows, optional, match, _key_map)
			when map_size(optional) == 0 and map_size(match) == 0 do
		rows
	end
	def match(caller_mod, user, rows, optional, match, key_map) do
		op = Map.merge(optional, match)
		map_size(key_map) > 0 || PrivateCandy.debug_remind(__ENV__, "没有外键对应关系")
		filter_map =
			Enum.reduce(
				rows,
				%{},
				fn (row, acc) ->
					Enum.reduce(
						op,
						acc,
						fn ({obj, keys}, acc2) ->
							key = key_map[obj]
							case Map.get(row, key, nil) do
								nil ->
									acc2
								v ->
									v = [v | Map.get(acc2, obj, [])]
									Map.put(acc2, obj, v)
							end
						end
					)
				end
			)
		obj_value_map = read(caller_mod, user, op, filter_map)
		if map_size(obj_value_map) > 0 do
			Enum.map(
				rows,
				fn (row) ->
					Enum.reduce(
						op,
						{row, nil},
						fn ({obj, _}, {acc, mod}) ->
							key = key_map[obj]
							mod = if mod == nil do
								caller_mod.list_mods()[obj]
							else
								mod
							end
							v = case MapLib.get_in(obj_value_map, [obj, Map.get(acc, key)]) do
								nil ->
									nil
								v ->
									case guard([v], mod, Map.get(op, obj)) do
										[h | _] ->
											h
										_ ->
											nil
									end
							end
							{Map.put(acc, obj, v), mod}
						end
					)
					|> elem(0)
				end
			)
		else
			rows
		end
	end

	def read(caller_mod, user, select, filter_map) do
		project_id = UserLib.get_project_id(user)
		Enum.reduce(
			select,
			%{},
			fn ({obj, keys}, acc) ->
				mod = caller_mod.list_mods()[obj]
				filter = Map.get(filter_map, obj, [])
				export = Kernel.function_exported?(mod, :read, 4)
				if is_list(filter) && filter != [] do
					if export || Pango.DbType.pg() == mod.db_type() do
						primary_key = mod.primary_key()
						filter = DeeprConvert.lookup_convert(mod.fields(), primary_key, filter)
						keys = [primary_key | keys]
									 |> Enum.uniq()
						list = mod.read(project_id, keys, [{primary_key, filter}], [return_json: []])
									 |> pass_pipelines(user, obj, mod)
									 |> MapLib.array2map(primary_key)
						if map_size(list) > 0 do
							Map.put(acc, obj, list)
						else
							acc
						end
					else
						{primary_key, _} = mod.primary_key()
						keys = [primary_key | keys]
									 |> Enum.uniq()
						filter = DeeprConvert.lookup_convert(mod.fields(), primary_key, filter)
						select_str = Enum.map_join(keys, ",", &(&1 <> ":" <> obj <> "." <> &1))
						list =
							"""
							match (#{obj}:#{mod.label()}) where #{obj}.#{primary_key} in $#{primary_key}
							return collect({#{select_str}}) as list
							"""
							|> Neo4jDb.get_one(%{primary_key => filter}, %{"list" => []})
							|> Map.get("list", [])
							|> pass_pipelines(user, obj, mod)
							|> MapLib.array2map(primary_key)
						if map_size(list) > 0 do
							Map.put(acc, obj, list)
						else
							acc
						end
					end
				else
					acc
				end
			end
		)
	end


	def guard(rows, mod, select) do
		case mod.mfa() do
			{m, f, a} ->
				apply(m, f, [:guard, mod, select, rows | a])
			_ ->
				rows
		end
	end

	def pass_pipelines(rows, user, object, mod) do
		case mod.pipelines() do
			%{"list" => {m, f, a}} ->
				case apply(m, f, [:after, user, object, rows | a]) do
					{new_list, _new} ->
						new_list
					new_list ->
						new_list
				end
			_ ->
				rows
		end
	end


end
